package aerolineas;

import java.util.*;

import colecciones.colecciones.MiColeccion;
import escalas.Escala;

import colecciones.bloques.BloqueExec;
import colecciones.bloques.BloqueTest;
import busquedas.CriteriosBusqueda;
import asientos.Vuelo;

public class Aerolineas {

	private List<Aerolinea> aerolineas = null;
	private CriteriosBusqueda tempCriterios;
	private List<Vuelo> listaVuelos1Tmp = null;
	private List<Vuelo> listaVuelos2Tmp = null;

	public Aerolineas() {
		this.aerolineas = new ArrayList<Aerolinea>();
		this.aerolineas.add(new LanchitaAdapter());
		this.aerolineas.add(new OceanicAdapter());
	}

	// WARNING: No fiarse de que los criterios de b�squeda sean cumplidos!!
	// (no podemos fiarnos porque tal vez las aerol�neas no nos permitan filtrar
	// lo que
	// necesitamos y devolvamos un conjunto mayor al pedido.
	private MiColeccion<Vuelo> getAsientos(CriteriosBusqueda criterios) {
		MiColeccion<Vuelo> asientos = new MiColeccion<Vuelo>();
		for (Aerolinea unaAerolinea : this.aerolineas) {
			asientos.addAll(unaAerolinea.asientosDisponibles(criterios));
		}
		return asientos;
	}

	// Este m�todo si garantiza que devuelve s�lo los asientos que cumplen con
	// todos
	// los criterios de b�squeda.
	public List<Vuelo> buscarAsientos(final CriteriosBusqueda criterios) {

		MiColeccion<Vuelo> vuelos = null;

		if (criterios.getConEscalas()) {
			tempCriterios = new CriteriosBusqueda().setOrigen(
					criterios.getOrigen()).setFechaSalida(
					criterios.getFechaSalida());
			vuelos = this.getAsientos(tempCriterios);

			// Me aseguro que tengan origen y fecha pedida
			MiColeccion<Vuelo> vuelosOrigen = vuelos.filter(
					new BloqueTest<Vuelo>() {
						@Override
						public boolean test(Vuelo unVuelo) {
							return unVuelo.getOrigen().equals(
									tempCriterios.getOrigen());
						}
					}).filter(new BloqueTest<Vuelo>() {
				@Override
				public boolean test(Vuelo unVuelo) {
					return unVuelo.getFechaSalida().equals(
							tempCriterios.getFechaSalida());
				}
			});
			// Busco los que son vuelos directos y los guardo en una lista.
			MiColeccion<Vuelo> vuelosDirectos = vuelosOrigen
					.filter(new BloqueTest<Vuelo>() {
						@Override
						public boolean test(Vuelo unVuelo) {
							return unVuelo.getDestino().equals(
									criterios.getDestino());
						}
					});

			// Vuelvo a pedir los asientos pero ahora para el segundo tramo con
			// origen en el destino de cada primer tramo.
			// Uso esta var global para pasarle los criterios originales al
			// bloque.
			tempCriterios = new CriteriosBusqueda().setFechaSalida(criterios
					.getFechaSalida());

			listaVuelos1Tmp = new ArrayList<Vuelo>();
			listaVuelos2Tmp = new ArrayList<Vuelo>();

			vuelosOrigen.forEach(new BloqueExec<Vuelo>() {
				@Override
				public void exec(final Vuelo unVuelo) {

					MiColeccion<Vuelo> vuelos2doTramo = Aerolineas.this
							.getAsientos(new CriteriosBusqueda()
									.setOrigen(unVuelo.getDestino()).setFechaSalida(unVuelo.getFechaLlegada()));
					// Filtro los vuelos que salen de donde llega el 1er tramo y
					// que salen despues de que llego el 1er tramo.
					// Tambien que sean de la misma aerolinea
					vuelos2doTramo = vuelos2doTramo.filter(
							new BloqueTest<Vuelo>() { // si cumple que sale desde donde llega el anterior
								@Override
								public boolean test(Vuelo tramo2) {
									return tramo2.getOrigen().equals(
											unVuelo.getDestino());
								}
							}).filter(new BloqueTest<Vuelo>() { // si cumple que sale despues de que llega el anterior
								@Override
								public boolean test(Vuelo tramo2) {
									return tramo2
											.getFechaSalida()
											.esMayorQue(
													unVuelo.getFechaLlegada());
								}
							}).filter(new BloqueTest<Vuelo>() { // si cumple que es de la misma aerolinea que el anterior
								@Override
								public boolean test(Vuelo tramo2) {
									return tramo2
											.getAerolinea().equals(
													unVuelo.getAerolinea());
								}
							});

					// Busco los itinerarios con una escala, 2 vuelos serian..
					MiColeccion<Vuelo> con1Escala = vuelos2doTramo
							.filter(new BloqueTest<Vuelo>() { // si cumple que llega al destino original
								@Override
								public boolean test(Vuelo tramo2) {
									return tramo2.getDestino().equals(
											criterios.getDestino());
								}
							});
					con1Escala.forEach(new BloqueExec<Vuelo>() {
						@Override
						public void exec(final Vuelo tramo2) {
							Escala itinerario1Escala = new Escala();
							itinerario1Escala.addVuelo(unVuelo);
							itinerario1Escala.addVuelo(tramo2);
							listaVuelos1Tmp.add(itinerario1Escala);

							/*
							 * Busco los itinerarios con dos escalas, 3 vuelos
							 * serian..
							 */
							MiColeccion<Vuelo> vuelos3erTramo = Aerolineas.this
									.getAsientos(new CriteriosBusqueda()
											.setOrigen(tramo2.getDestino()).setFechaSalida(tramo2.getFechaLlegada()));
							// Filtro los vuelos que salen de donde llega el 2do
							// tramo y que salen despues de que llego el 2do
							// tramo.
							vuelos3erTramo = vuelos3erTramo.filter(
									new BloqueTest<Vuelo>() { // si cumple que sale desde donde llega el anterior
										@Override
										public boolean test(Vuelo tramo3) {
											return tramo3.getOrigen().equals(
													tramo2.getDestino());
										}
									}).filter(new BloqueTest<Vuelo>() { // si cumple que sale despues de que llega el anterior
										@Override
										public boolean test(Vuelo tramo3) {
											return tramo3
													.getFechaSalida()
													.esMayorQue(
															tramo2.getFechaLlegada());
										}
									}).filter(new BloqueTest<Vuelo>() { // si cumple que es de la misma aerolinea que el anterior
										@Override
										public boolean test(Vuelo tramo3) {
											return tramo3
													.getAerolinea().equals(
															tramo2.getAerolinea());
										}
									});

							MiColeccion<Vuelo> con2Escalas = vuelos3erTramo
									.filter(new BloqueTest<Vuelo>() { // si cumple que llega al destino original
										@Override
										public boolean test(Vuelo tramo3) {
											return tramo3.getDestino().equals(
													criterios.getDestino());
										}
									});
							con2Escalas.forEach(new BloqueExec<Vuelo>() {
								@Override
								public void exec(Vuelo tramo3) {
									Escala itinerario2Escalas = new Escala();
									itinerario2Escalas.addVuelo(unVuelo);
									itinerario2Escalas.addVuelo(tramo2);
									itinerario2Escalas.addVuelo(tramo3);
									listaVuelos2Tmp.add(itinerario2Escalas);
								}

							});
						}
					});

				}

			});

			// Seteo vuelos con los vuelos directos + los vuelos con 1 escala +
			// los vuelos con 2 escalas.
			vuelos = vuelosDirectos.copy().addAll(listaVuelos1Tmp)
					.addAll(listaVuelos2Tmp);

		} else { //////// SIN ESCALAS:
			tempCriterios = criterios;
			vuelos = this.getAsientos(criterios);

			vuelos = vuelos.filter(new BloqueTest<Vuelo>() {
				@Override
				public boolean test(Vuelo unAsiento) {
					return unAsiento.getDestino().equals(
							tempCriterios.getDestino());
				}
			});

			vuelos = vuelos.filter(new BloqueTest<Vuelo>() {
				@Override
				public boolean test(Vuelo unAsiento) {
					return unAsiento.getOrigen().equals(
							tempCriterios.getOrigen());
				}
			}).filter(new BloqueTest<Vuelo>() {
				@Override
				public boolean test(Vuelo unAsiento) {
					return unAsiento.getFechaSalida().equals(
							tempCriterios.getFechaSalida());
				}
			});

		}//////// END SIN ESCALAS
		List<Vuelo> ret = vuelos.asArrayList();
		criterios.getFiltroOpcional().aplicarFiltroOpcional(ret);
		return ret;
	}
}
